<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="C++,template," />










<meta name="description" content="前言本文讲述了C++中组织模板源代码的几种方式——包含模型、显示实例化和分离模型。来源是《C++ templates》。本文中的部分代码可从此链接下载，提取码为4xxq（非书中所带，有错请海涵）。">
<meta property="og:type" content="article">
<meta property="og:title" content="C++模板代码组织方式">
<meta property="og:url" content="http://liuyun000.gitee.io/2020/01/10/C++/C++%E6%A8%A1%E6%9D%BF%E4%BB%A3%E7%A0%81%E7%BB%84%E7%BB%87%E6%96%B9%E5%BC%8F/index.html">
<meta property="og:site_name" content="liuyun的博客">
<meta property="og:description" content="前言本文讲述了C++中组织模板源代码的几种方式——包含模型、显示实例化和分离模型。来源是《C++ templates》。本文中的部分代码可从此链接下载，提取码为4xxq（非书中所带，有错请海涵）。">
<meta property="og:image" content="http://liuyun000.gitee.io/images/%E9%93%BE%E6%8E%A5%E9%94%99%E8%AF%AF.jpg">
<meta property="article:published_time" content="2020-01-09T16:00:00.000Z">
<meta property="article:modified_time" content="2020-01-10T04:52:14.206Z">
<meta property="article:author" content="liuyun">
<meta property="article:tag" content="C++">
<meta property="article:tag" content="template">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://liuyun000.gitee.io/images/%E9%93%BE%E6%8E%A5%E9%94%99%E8%AF%AF.jpg">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://liuyun000.gitee.io/2020/01/10/C++/C++模板代码组织方式/"/>





  <title>C++模板代码组织方式 | liuyun的博客</title>
  








<meta name="generator" content="Hexo 4.2.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">liuyun的博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/%20" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://liuyun000.gitee.io/2020/01/10/C++/C++%E6%A8%A1%E6%9D%BF%E4%BB%A3%E7%A0%81%E7%BB%84%E7%BB%87%E6%96%B9%E5%BC%8F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="liuyun">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="liuyun的博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">C++模板代码组织方式</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-01-10T00:00:00+08:00">
                2020-01-10
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/C/" itemprop="url" rel="index">
                    <span itemprop="name">C++</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>本文讲述了C++中组织模板源代码的几种方式——包含模型、显示实例化和分离模型。来源是《C++ templates》。本文中的部分代码可从<a href="https://pan.baidu.com/s/1dBUlZfFRHU5hJl-RSuf6Nw" target="_blank" rel="noopener">此链接</a>下载，提取码为4xxq（非书中所带，有错请海涵）。</p>
<a id="more"></a>

<h3 id="1-包含模型"><a href="#1-包含模型" class="headerlink" title="1. 包含模型"></a>1. 包含模型</h3><p>我们可以用几种方法来组织模板源代码，最常用的方法是<strong>包含模型</strong>(inclusion model)。</p>
<h4 id="1-1-链接器错误"><a href="#1-1-链接器错误" class="headerlink" title="1.1 链接器错误"></a>1.1 链接器错误</h4><p>大多数C和C++程序员会这样组织他们的非模板代码：</p>
<ul>
<li>类(class)和其他类型(other types)都被放在一个头文件中。通常而言，头文件是一个扩展名为.hpp（或者.H、.h、.hh、.hxx）的文件</li>
<li>对于全局变量和（非内联）函数，只有声明放在头文件中，定义则位于dot-C文件，通常而言，dot-C文件是指扩展名为.cpp（或者.C、.c、.cc、.cxx）的文件。</li>
</ul>
<p>这样一切都可以正常运作了。所需的类型定义在整个程序中都是可见的；并且对于变量和函数而言，连接器也不会给出重复定义的错误。</p>
<p>当牢记了这种约定之后，刚开始接触模板的程序员却总会对这种约定发出抱怨，因为它令链接器产生了一个错误。我们可以通过下面的错误小程序来说明一点。利用上面针对普通代码的约定，我们应该在一个头文件中声明模板：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//  error/myfirst.hpp</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> MYFIRST_HPP</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MYFIRST_HPP</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//模板声明</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_typeof</span><span class="params">(T <span class="keyword">const</span>&amp;)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span>	<span class="comment">//MYFIRST_HPP</span></span></span><br></pre></td></tr></table></figure>

<p>print_typeof()是一个辅助函数模板的声明，它输出某些类型信息。该函数模板的实现被放在下面的dot-C文件里面：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//   error/myfirst.cpp</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;typeinfo&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"myfirst.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//模板的实现/定义</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_typeof</span><span class="params">(T <span class="keyword">const</span>&amp; x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="keyword">typeid</span>(x).name() &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最后，我们在另一个dot-C文件里使用这个模板，并把模板声明包含进这个文件：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//  error/myfristmain.cpp</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"myfirst.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//使用模板</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">double</span> ice = <span class="number">3.0</span>;</span><br><span class="line">    print_typeof(ice);	<span class="comment">//调用参数类型为double 的函数模板</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>大多数C++编译器都会顺利地接收这个程序：但是链接器可能会报错，提示找不到函数print_typeof()的定义。</p>
<p><img src="/images/%E9%93%BE%E6%8E%A5%E9%94%99%E8%AF%AF.jpg" alt="g++链接器报错"></p>
</br>

<p>事实上，这个错误的原因在于：函数模板print_typeof()的定义还没有被实例化。为了使模板真正得到实例化，编译器必须知道：应该实例化哪个定义以及要基于那个模板实参来进行实例化。遗憾的是，在前面的例子里，这两部分信息位于分开编译的不同文件里面。因此，当我们的编译器看到print_typeof()调用，但还没有看到基于double实例化的函数定义的时候，它只是假设在别处提供了这个定义，并产生一个指向该定义的引用（让链接器利用该引用来解决这个问题）。另一方面，当编译器处理文件myfirst.cpp的时候，他并没有指出：编译器必须基于特定实参所包含的模板定义进行实例化。</p>
</br>

<h4 id="1-2-头文件中的模板"><a href="#1-2-头文件中的模板" class="headerlink" title="1.2 头文件中的模板"></a>1.2 头文件中的模板</h4><p>对于前面的问题，我们通常是采取对待宏或内联函数的解决办法：我们把模板的定义也包含在声明模板的头文件里面，即让定义和声明都为一同一个头文件中。对于上面的例子，我们可以通过把：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"myfirst.cpp"</span></span></span><br></pre></td></tr></table></figure>

<p>添加到myfirst.hpp的末尾，或者在每个使用模板的dot-C文件都包含myfirst.cpp。显然，第三种方法就是完全不要myfirst.cpp，然后重写myfirst.hpp，让它同时包含模板声明和模板定义：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//  inclusion_module/myfirst2.hpp</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> MYFIRST_HPP</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MYFIRST_HPP</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 模板声明</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_typeof</span><span class="params">(T <span class="keyword">const</span>&amp;)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模板实现/定义</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_typeof</span><span class="params">(T <span class="keyword">const</span>&amp; x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="keyword">typeid</span>(x).name() &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span>	<span class="comment">//MYFIRST_HPP</span></span></span><br></pre></td></tr></table></figure>

<p>我们称模板的这种组织方式为包含模型。通过使用这种模型，你会发现前面的程序可以顺利编译、链接和运行。</p>
<p>针对这一点，我们可以得出一些结论，<strong>包含模型明显增加了包含头文件myfirst,hpp的开销，这也是包含模型最大的不足之处</strong>。在例子中，主要的开销并不是取决于模板定义本身的大小，而在于模板定义中所包含的哪些头文件（在我们的例子中是&lt;iostream&gt;和&lt;typeinfo&gt;）的大小。你或许已经知道这样会带来成千上万行的代码，因为每个诸如&lt;iostream&gt;的头文件本身都包含了许多类似的模板定义。</p>
<p>在实际应用中，这是一个很严重的问题，因为它大大增加了编译复杂程序所耗费的时间。然而，现在的程序大多已经不需要在编译和链接上花上几个小时，将来就更不用说了。</p>
<p>如果不需要考虑创建期的时间问题，建议你尽量使用包含模型来组织模板代码。我们在后面会考察两外两种组织模板的方式，但就我们（《C++ templates》作者）的观点来看，另外两种组织方式的实际缺陷往往比这里所讨论的创建期开销更加严重。当然，这两种组织方式也有其他一些与软件开发的应用方面简介相关的优点。</p>
<p>从包含模型得出的另一个（更微妙）结论是，非内联函数模板与“内联函数和宏”有一个很重要的区别，那就是非内联函数模板在调用的位置并不会被扩展，而是当他们基于某种类型进行实例化之后，才会产生一份新的（基于该类型的）函数拷贝。因为这（产生函数拷贝）是一个自动化过程，所以在编译结束的时候，编译器可能会在不同的文件里产生两份拷贝，于是，当链接器发现同一个函数具有两种不同的定义时，就会报告一个错误，理论上讲，这并不是我们需要关心的问题，他应该由C++编译系统来解决。而且，事实上大多数情况下都不会出现这种问题，我们根本没有必要太过于在意这个问题。</p>
<p>最后，我们需要指出的是：在我们例子中应用到普通模板的所有特定，对类模板的成员函数和静态数据成员、成员函数模板也都是适用的。</p>
</br>

</br>

<h3 id="2-显式实例化"><a href="#2-显式实例化" class="headerlink" title="2. 显式实例化"></a>2. 显式实例化</h3><p>包含模型能够确保所有需要的模板都已经实例化。这是因为：当需要进行实例化的时候，C++编译系统会自动产生所对应的实例化体。另外，C++标准还提供了一种手工实例化模板的机制：显式实例化指示符（explicit instantiation directive）。</p>
<h4 id="2-1-显式实例化的例子"><a href="#2-1-显式实例化的例子" class="headerlink" title="2.1 显式实例化的例子"></a>2.1 显式实例化的例子</h4><p>为了说明手工实例化，让我们回顾那个导致链接器错误的例子。在此，为了避免这个链接期错误，我们可以通过给程序添加下面的文件：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//  inst/myfirstinst.cpp</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"myfirst.cpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//基于类型double显式实例化print_typeof()</span></span><br><span class="line"><span class="keyword">template</span> <span class="keyword">void</span> print_typeof&lt;<span class="keyword">double</span>&gt;(<span class="keyword">double</span> <span class="keyword">const</span>&amp;);</span><br></pre></td></tr></table></figure>

<p><strong>显示实例化指示符由关键字template和紧跟其后的我们所需要实例化的实体（可以是类、函数、成员函数等）的声明组成，而且，该声明是一个已经用实参完全替换参数之后的声明。</strong>在我们的例子中，我们针对的是一个普通函数，但该指示也适用于成员函数和静态数据成员。譬如</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//基于int显示实例化MyClass&lt;&gt;的构造函数</span></span><br><span class="line"><span class="keyword">template</span> MyClass&lt;<span class="keyword">int</span>&gt;::MyClass();</span><br><span class="line"></span><br><span class="line"><span class="comment">//基于int显式实例化函数max();</span></span><br><span class="line"><span class="function"><span class="keyword">template</span> <span class="keyword">int</span> <span class="keyword">const</span>&amp; <span class="title">max</span><span class="params">(<span class="keyword">int</span> <span class="keyword">const</span>&amp;, <span class="keyword">int</span> <span class="keyword">const</span>&amp;)</span></span>;</span><br></pre></td></tr></table></figure>

<p>你还可以显示实例化类模板，这样就可以同时实例化它的所有类成员。但有一点需要注意：对于这些在前面已经实例化的成员，就不能再次对它们进行实例化：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//基于int显示实例化类Stack&lt;&gt;</span></span><br><span class="line"><span class="keyword">template</span> Stack&lt;<span class="keyword">int</span>&gt;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//基于string显示实例化Stack&lt;&gt;的某些成员函数</span></span><br><span class="line"><span class="keyword">template</span> Stack&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt;::Stack();</span><br><span class="line"><span class="keyword">template</span> <span class="keyword">void</span> Stack&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt;::push(<span class="built_in">std</span>::<span class="built_in">string</span> <span class="keyword">const</span>&amp;);</span><br><span class="line"><span class="keyword">template</span> <span class="built_in">std</span>::<span class="built_in">string</span> Stack&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt;::top() <span class="keyword">const</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//错误：对于前面已经显式实例化过的成员函数，不能再次对它进行显式实例化</span></span><br><span class="line"><span class="keyword">template</span> Stack&lt;<span class="keyword">int</span>&gt;::Stack();</span><br></pre></td></tr></table></figure>

<p>对于每个不同实体，在一个程序中最多只能有一个显式实例化实体，换句话说，你可以同时显式实例化print_typeof&lt;int&gt;和print_typeof&lt;double&gt;，但在同一个程序中每个指示符都只能出现一次（就是不能出现两个print_typeof&lt;int&gt;）。如果不遵循这条规则，通常都会导致链接错误，链接器会报错：发现了实例化实体的重复定义。</p>
<p>人工实例化有一个显著的缺点：我们必须仔细跟踪每个需要实例化的实体。对于大项目而言，这种跟踪很快就会带来巨大负担：因此，我们并不建议使用这种方法。</p>
</br>

</br>

<h3 id="3-分离模型"><a href="#3-分离模型" class="headerlink" title="3. 分离模型"></a>3. 分离模型</h3><p>前面给出的两种方法都可以正常工作，也完全符合C++标准。然而，标准还给出了另一种机制：导出模板(export template)。这种机制通常也被称为C++模板的分离模型(separation model)。</p>
<h4 id="3-1-关键字export"><a href="#3-1-关键字export" class="headerlink" title="3.1 关键字export"></a>3.1 关键字export</h4><p>大体上讲，关键字export的功能使用是非常简单的：在一个文件里面定义模板，并在模板的定义和（非定义的）声明的前面加上关键字export。对于上面的例子，通过使用export，我们会得到下面的函数模板声明：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//  export/myfirst3.hpp</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> MYFIRST_HPP</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MYFIRST_HPP</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_typeof</span><span class="params">(T <span class="keyword">const</span>&amp;)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span>	<span class="comment">//MYFIRST_HPP</span></span></span><br></pre></td></tr></table></figure>

<p>即使在模板定义不可见的条件下，被导出的模板也可以正常使用。换句话说，使用模板的位置和模板定义的位置可以在不同的翻译单元中。在我们的例子中，文件myfirst3.hpp现在只是包含模板的成员函数的声明，但对于使用这些成员已经足够了。和刚开始导致编译器报错的那个例子相比，我们只是在代码中添加了关键字export，一切就可以顺利通过了。</p>
<p>在一个预处理文件内部（就是指在一个翻译单元内部），我们只需要在第一个证明前面标记export关键字就可以了，后面的重新声明会隐式地保留这个export特性。这也是我们不需要修改文件myfirst.cpp的原因所在。就是说，myfirst.cpp文件里面的这个定义是隐式export，因为在它#include的头文件myfirst3.hpp里面，该定义所对应的声明已经被限定为export的了。另一方面，在模板定义中提供一个冗余的export也是可取的，因为这样可以提高代码的可读性。</p>
<p>实际上关键字export可以应用于函数模板、类模板和成员函数、成员函数模板和类模板的静态数据成员。另外，它还可以用于类模板的声明，这意味着每个可导出的类成员都被看做可导出实体，但类模板本身实际上并没有被导出（因此，类模板的定义仍需要出现在头文件中）。你仍可以隐式或者显式地定义内联成员函数。然而，内联函数确实不可导出的。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span>&#123;</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">    	<span class="function"><span class="keyword">void</span> <span class="title">memfun1</span><span class="params">()</span></span>;		<span class="comment">//被导出(exproted)的函数</span></span><br><span class="line">    	<span class="function"><span class="keyword">void</span> <span class="title">memfun2</span><span class="params">()</span></span>&#123;		<span class="comment">//隐式内联不能被导出</span></span><br><span class="line">            ...</span><br><span class="line">		&#125;</span><br><span class="line">    	<span class="function"><span class="keyword">void</span> <span class="title">memfun3</span><span class="params">()</span></span>;		<span class="comment">//显式内联不能被导出</span></span><br><span class="line">    	...</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">inline</span> <span class="keyword">void</span> MyClass&lt;T&gt;::memfun3()</span><br><span class="line">&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>另外，export关键字不能和inline关键字一起使用：如果用于模板的话，export要位于关键字template前面，譬如下面的程序就是非法的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Invalid</span>&#123;</span></span><br><span class="line">	<span class="keyword">public</span>:</span><br><span class="line">    	<span class="function"><span class="keyword">export</span> <span class="keyword">void</span> <span class="title">wrong</span><span class="params">(T)</span></span>;	<span class="comment">//错误：export没有位于template之前</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;		<span class="comment">//错误：同时使用了export和inline</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> T <span class="keyword">const</span>&amp; <span class="title">max</span><span class="params">(T <span class="keyword">const</span>&amp; a, T <span class="keyword">const</span>&amp; b)</span>	<span class="comment">//错误：同时使用了export和inline</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a &lt; b ? b : a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="3-2-分离模型的限制"><a href="#3-2-分离模型的限制" class="headerlink" title="3.2 分离模型的限制"></a>3.2 分离模型的限制</h4><p>谈到这里，你可能会觉得奇怪：既然导出模板（exported template）可以很好地解决最初的问题，我们为何仍然建议建使用包含模型呢。事实上，export关键字还有其他一方面的影响。</p>
<p>一方面，几乎没有编译器实现。</p>
<p>其次，export虽然看起来几乎是完美无缺的，但它实际上还是有一些缺点的。</p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/C/" rel="tag"># C++</a>
          
            <a href="/tags/template/" rel="tag"># template</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/11/22/%E5%AF%86%E7%A0%81%E5%AD%A6/miracl%E5%BA%93%E5%AE%9E%E7%8E%B0ElGamal%E5%8A%A0%E8%A7%A3%E5%AF%86/" rel="next" title="miracl库实现ElGamal加解密">
                <i class="fa fa-chevron-left"></i> miracl库实现ElGamal加解密
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2020/01/11/others/win10%E8%87%AA%E5%B8%A6%E8%BE%93%E5%85%A5%E6%B3%95%E6%89%93%E4%B8%8D%E5%87%BA%E4%B8%AD%E6%96%87%E6%A0%87%E7%82%B9/" rel="prev" title="win10自带输入法打不出中文标点">
                win10自带输入法打不出中文标点 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">liuyun</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/%20%7C%7C%20archive">
              
                  <span class="site-state-item-count">16</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">9</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">21</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#前言"><span class="nav-number">1.</span> <span class="nav-text">前言</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-包含模型"><span class="nav-number">2.</span> <span class="nav-text">1. 包含模型</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-1-链接器错误"><span class="nav-number">2.1.</span> <span class="nav-text">1.1 链接器错误</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#1-2-头文件中的模板"><span class="nav-number">2.2.</span> <span class="nav-text">1.2 头文件中的模板</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-显式实例化"><span class="nav-number">3.</span> <span class="nav-text">2. 显式实例化</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-显式实例化的例子"><span class="nav-number">3.1.</span> <span class="nav-text">2.1 显式实例化的例子</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-分离模型"><span class="nav-number">4.</span> <span class="nav-text">3. 分离模型</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-1-关键字export"><span class="nav-number">4.1.</span> <span class="nav-text">3.1 关键字export</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-分离模型的限制"><span class="nav-number">4.2.</span> <span class="nav-text">3.2 分离模型的限制</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">liuyun</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginModelPath":"assets/","model":{"jsonPath":"/live2dw/assets/miku.model.json"},"display":{"position":"right","width":150,"height":300},"mobile":{"show":false},"log":false,"pluginJsPath":"lib/","pluginRootPath":"live2dw/","tagMode":false});</script></body>
</html>
